/*
 * ArtifactTester.java CS 3500 :: Fall 2009 Test Program for Artifact
 * ADT', Homework 2
 */
import java.util.Random;

public class ArtifactTester {
  /**
   * Main method After compiling it can be run with: java
   * ArtifactTester Or imported into Eclipse
   */
  public static void main(String[] args) {
    int totalInstances;
    int totalErrors;
    totalInstances = 100;
    totalErrors = testRandomCreate(totalInstances);
    /* A few Stats... */
    System.out.println("\nNumber of artifacts tested: " + totalInstances);
    System.out.println("Number of errors found: " + totalErrors);
  }
  
  // Test 'num' random instances of Artifact
  // Returns the number of errors
  private static int testRandomCreate(int num) {
    int count = num;
    int errors = 0;
    while (count > 0) {
      errors = errors + testSingleRandomCreate();
      count = count - 1;
    }
    return errors;
  }
  
  // Returns a random integer between 0 and max, inclusive
  private static int randomInt(int max) {
    return new Random().nextInt(max + 1);
  }
  
  private static int testSingleRandomCreate() {
    int sx = randomInt(1000);
    int sy = randomInt(1000);
    String name = "art" + randomInt(1000);
    Artifact a = Artifact.create(Coord.create(sx, sy), name);
    return testCreate(a, sx, sy, name);
  }
  
  public static boolean equalCoords(Coord c1, Coord c2) {
    return (c1.xPos() == c2.xPos() && c1.yPos() == c2.yPos());
  }
  
  public static boolean equalArtifacts(Artifact a1, Artifact a2) {
    return equalCoords(a1.position(), a2.position()) && a1.name() == a2.name();
  }
  
  private static int testCreate(Artifact a, int sx, int sy, String name) {
    int errors = 0;
    String front = "Artifact.create(Coord.create(" + sx + "," + sy + "),"
                   + name + ").";
    try {
      // create(c,n).position() = c
      if (!(assertT(a.position().equals(Coord.create(sx, sy)), front
                                                               + "position()")))
        errors++;
      // create(c,n).name() = n
      if (!(assertT(a.name().equals(name), front + "name()")))
        errors++;
      // create(c,n).move(dx,dy) = create(c.move(dx,dy),n)
      int dx, dy;
      do {
        dx = randomInt(1000) - 500;
        dy = randomInt(1000) - 500;
      } while (sx + dx < 0 || sy + dy < 0);
      if (!(assertT(equalArtifacts(a.move(dx, dy),
                                   Artifact.create(Coord.create(sx + dx, 
                                                                sy + dy),
                                                   a.name())), front + "move("
                                                               + dx + "," + dy
                                                               + ")")))
        errors++;
      // create(c,n).toString() = n + c.toString()
      if (!(assertT(a.toString().equals(name + a.position().toString()),
                    front + "toString()")))
        errors++;
      // create(c,n).equals(a)
      // = true if c.equals(a.position())=true and
      // n.equals(a.name())=true
      // = false otherwise
      if (!(assertT(equalArtifacts(a, Artifact.create(Coord.create(sx, sy),
                                                      name)),
                    front + "equals(Artifact)")))
        errors++;
      // create(c,n).equals(obj) = false [when obj is not an Artifact]
      if (!assertT(!a.equals(new Object()), front
                                            + "equals(Object, non-Artifact)"))
        errors++;
      // create(c,n).hashCode() = c.hashCode() + n.hashCode()
      if (!(assertT(a.hashCode() == 
                    a.position().hashCode() + a.name().hashCode(), 
                    front + "hashCode()")))
        errors++;
      return errors;
    } catch (RuntimeException ex) {
      /*
       * If there was an exception anywhere in there, then we have a
       * problem
       */
      assertT(false, "Exception: " + ex.getMessage());
      return errors + 1;
    }
  }
  
  /*
   * Result is expected to be true for passing tests, and false for
   * failing tests. If a test fails, we print out the provided message
   * so the user can see what might have gone wrong. Be sure to review
   * anything that doesn't make sense.
   */
  /* returns true if the assertion passed, and false otherwise! */
  private static boolean assertT(boolean result, String msg) {
    if (!result) {
      System.out.println("\n**ERROR**: " + msg);
    }
    System.out.print(".");
    return result;
  }
}
